/**
 * @author David Young
 */
package csab.core.utility;

import java.util.List;

import org.newdawn.slick.SlickException;

import csab.core.Entity;
import csab.core.ResourceManager;
import csab.core.Sprite;

/**
 * This class is used to spawn Sprite objects to the game world based
 * on certain parameters.
 * @author David Young
 */
public class SpawnerFactory implements Observer {
	private int x, y;
	private int currspawncount;
	//Maximum number of simultaneous spawns per spawner
	private int maxspawns;
	//Minimum number of spawned entities at any given time
	private int minspawns;
	//Time in milliseconds between spawns
	private int minTBtwnSpawns;
	//Chance that a spawn will occur (set this to a low value unless you 
	//to spawn an object as soon as the spawner is ready to make one)
	private float spawnchance;
	//Current delta total since the last spawn
	private int totalDelta;
	private int totalspawncount;
	//Maximum number of spawns per any instance of a spawner
	private int totalspawnlimit;
	//Type of entity to spawn
	private String type;
	private ResourceManager mgr;
	/**
	 * Constructor for SpawnerFactory.
	 * @param tsl The total number of spawns this SpawnerFactory
	 * can produce over its lifetime.
	 * @param mbt The minimum amount of time (in milliseconds) that must
	 * pass in-between spawns. This is ignored if the mins requirement is
	 * not met or the maxs limit is reached.
	 * @param chance The chance that this SpawnerFactory will spawn a new
	 * Sprite after a given amount of time (on a call to {@link #update(List, int) update}).
	 * The range is [0,1].
	 * @param type The type of Sprite to spawn from this factory.
	 * @throws SlickException 
	 * @see csab.core.Sprite
	 */
	public SpawnerFactory(int tsl, int mbt, float chance, String type ) throws SlickException
	{
		mgr = ResourceManager.getManager("resource/resources.xml");
		init(0, 0, tsl, mbt, chance, type);
	}
	
	/**
	 * Constructor for SpawnerFactory.
	 * @param maxs The maximum number of simultaneous spawns.
	 * @param tsl The total number of spawns this SpawnerFactory
	 * can produce over its lifetime.
	 * @param mbt The minimum amount of time (in milliseconds) that must
	 * pass in-between spawns. This is ignored if the mins requirement is
	 * not met or the maxs limit is reached.
	 * @param chance The chance that this SpawnerFactory will spawn a new
	 * Sprite after a given amount of time (on a call to {@link #update(List, int) update}).
	 * The range is [0,1].
	 * @param type The type of Sprite to spawn from this factory.
	 * @throws SlickException 
	 * @see csab.core.Sprite
	 */
	public SpawnerFactory(int maxs, int tsl, int mbt, float chance, String type) throws SlickException
	{
		init(0, maxs, tsl, mbt, chance, type);
	}
	
	/**
	 * Constructor for SpawnerFactory.
	 * @param mins The minimum number of simultaneous spawns.
	 * @param maxs The maximum number of simultaneous spawns.
	 * @param tsl The total number of spawns this SpawnerFactory
	 * can produce over its lifetime.
	 * @param mbt The minimum amount of time (in milliseconds) that must
	 * pass in-between spawns. This is ignored if the mins requirement is
	 * not met or the maxs limit is reached.
	 * @param chance The chance that this SpawnerFactory will spawn a new
	 * Sprite after a given amount of time (on a call to {@link #update(List, int) update}).
	 * The range is [0,1].
	 * @param type The type of Sprite to spawn from this factory.
	 * @throws SlickException 
	 * @see csab.core.Sprite
	 */
	public SpawnerFactory(int mins, int maxs, int tsl, int mbt, float chance, String type) throws SlickException
	{
		init(mins, maxs, tsl, mbt, chance, type);
	}
	
	/* (non-Javadoc)
	 * @see csab.core.utility.Observer#handleObservation(csab.core.utility.ObserverMessage)
	 */
	@Override
	public void handleObservation(ObserverMessage msg) {
		if(msg.getMessage().compareToIgnoreCase(Entity.DESTROYED_MSG) == 0)
		{
			currspawncount--;
		}
	}
	
	/**
	 * This method appends Sprites to the end of a list based on
	 * the parameters of the owning SpawnerFactory object.
	 * @param append The list to append Sprites to.
	 * @param delta The time in milliseconds since the last update.
	 * @throws SlickException 
	 */
	public void update(List<Sprite> append, int delta) throws SlickException
	{
		totalDelta += delta;
		if((totalspawnlimit == 0 || totalspawncount < totalspawnlimit) && 
		   (maxspawns == 0 || currspawncount < maxspawns))
		{
			if(currspawncount < minspawns)
			{ 
				Sprite temp = mgr.getSprite(type);
				temp.addObserver(this);
				temp.setX(this.x);
				temp.setY(this.y);
				append.add(temp);
				totalDelta = 0;
				totalspawncount++;
				currspawncount++;
			}
			else if(totalDelta > minTBtwnSpawns)
			{
				float range = (int)(spawnchance * 100);
				float val = (int)(Math.random() * 100);
				if(val < range)
				{
					Sprite temp = mgr.getSprite(type); 
					temp.addObserver(this);
					temp.setX(this.x);
					temp.setY(this.y);
					append.add(temp);
					totalDelta = 0;
					totalspawncount++;
					currspawncount++;
				}
			}
		}
	}

	private void init(int mins, int maxs, int tsl, int mbt, float chance, String type) throws SlickException
	{
		this.minspawns = mins;
		this.maxspawns = maxs;
		this.totalspawnlimit = tsl;
		this.minTBtwnSpawns = mbt;
		this.spawnchance = chance;
		this.type = type;
		this.currspawncount = 0;
		this.totalspawncount = 0;
		this.totalDelta = 0;
		this.mgr = ResourceManager.getManager("resource/resources.xml");
	}

	/**
	 * Get the x location of this spawner.
	 * @return The x-axis position of this object.
	 */
	public int getX() {
		return x;
	}

	/**
	 * Set the x location of this spawner.
	 * @param x The new x-axis position of this object.
	 */
	public void setX(int x) {
		this.x = x;
	}

	/**
	 * Get the y location of this spawner.
	 * @return The y-axis position of this object.
	 */
	public int getY() {
		return y;
	}

	/**
	 * Set the y location of this spawner.
	 * @param y The new y-axis position of this object.
	 */
	public void setY(int y) {
		this.y = y;
	}
}
